//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <util.h>
#include <udelay.h>
#include <gpio.h>
#include <bulverde.h>

//
// Definitons for camera
//

#define CICR0   __REG(0x50000000)
#define CICR1   __REG(0x50000004)
#define CICR2   __REG(0x50000008)
#define CICR3   __REG(0x5000000C)
#define CICR4   __REG(0x50000010)
#define CISR    __REG(0x50000014)
#define CIFR    __REG(0x50000018)
#define CITOR   __REG(0x5000001C)
#define CIBR0   __REG(0x50000028)
#define CIBR1   __REG(0x50000030)
#define CIBR2   __REG(0x50000038)

#define XLLP_CLKEN_CAMERA (0x1u << 24)

enum XLLP_CI_REGS {
        XLLP_CI_REG_SIZE = 0x04U,    //Size of each CI register (below) = 4 bytes

        XLLP_CICR0 = 0x00,           //Control
        XLLP_CICR1 = 0x04,
        XLLP_CICR2 = 0x08,
        XLLP_CICR3 = 0x0C,
        XLLP_CICR4 = 0x10,

        XLLP_CISR  = 0x14,           //Status
        XLLP_CIFR  = 0x18,           //FIFO Control
        XLLP_CITOR = 0x1C,           //Time-out

        XLLP_CIBR0 = 0x28,           //Receive Buffer
        XLLP_CIBR1 = 0x30,
        XLLP_CIBR2 = 0x38
};

enum XLLP_CI_REGBITS_CICR0 {
        XLLP_CI_CICR0_FOM       = 0x00000001,
        XLLP_CI_CICR0_EOFM      = 0x00000002,
        XLLP_CI_CICR0_SOFM      = 0x00000004,
        XLLP_CI_CICR0_CDM       = 0x00000008,
        XLLP_CI_CICR0_QDM       = 0x00000010,
        XLLP_CI_CICR0_PERRM     = 0x00000020,
        XLLP_CI_CICR0_EOLM      = 0x00000040,
        XLLP_CI_CICR0_FEM       = 0x00000080,
        XLLP_CI_CICR0_RDAVM     = 0x00000100,
        XLLP_CI_CICR0_TOM       = 0x00000200,
        XLLP_CI_CICR0_RESERVED  = 0x03FFFC00,
        XLLP_CI_CICR0_SIM_SHIFT = 24,
        XLLP_CI_CICR0_SIM_SMASK = 0x7,
        XLLP_CI_CICR0_DIS       = 0x08000000,
        XLLP_CI_CICR0_ENB       = 0x10000000,
        XLLP_CI_CICR0_SL_CAP_EN = 0x20000000,
        XLLP_CI_CICR0_PAR_EN    = 0x40000000,
        XLLP_CI_CICR0_DMA_EN    = 0x80000000,
        XLLP_CI_CICR0_INTERRUPT_MASK = 0x3FF
};

enum XLLP_CI_REGBITS_CICR1 {
        XLLP_CI_CICR1_DW_SHIFT       = 0,
        XLLP_CI_CICR1_DW_SMASK       = 0x7,
        XLLP_CI_CICR1_COLOR_SP_SHIFT = 3,
        XLLP_CI_CICR1_COLOR_SP_SMASK = 0x3,
        XLLP_CI_CICR1_RAW_BPP_SHIFT  = 5,
        XLLP_CI_CICR1_RAW_BPP_SMASK  = 0x3,
        XLLP_CI_CICR1_RGB_BPP_SHIFT  = 7,
        XLLP_CI_CICR1_RGB_BPP_SMASK  = 0x7,
        XLLP_CI_CICR1_YCBCR_F        = 0x00000400,
        XLLP_CI_CICR1_RBG_F          = 0x00000800,
        XLLP_CI_CICR1_RGB_CONV_SHIFT = 12,
        XLLP_CI_CICR1_RGB_CONV_SMASK = 0x7,
        XLLP_CI_CICR1_PPL_SHIFT      = 15,
        XLLP_CI_CICR1_PPL_SMASK      = 0x7FF,
        XLLP_CI_CICR1_RESERVED       = 0x1C000000,
        XLLP_CI_CICR1_RGBT_CONV_SHIFT= 29,
        XLLP_CI_CICR1_RGBT_CONV_SMASK= 0x3,
        XLLP_CI_CICR1_TBIT           = 0x80000000
};

enum XLLP_CI_REGBITS_CICR2 {
        XLLP_CI_CICR2_FSW_SHIFT = 0,
        XLLP_CI_CICR2_FSW_SMASK = 0x3,
        XLLP_CI_CICR2_BFPW_SHIFT= 3,
        XLLP_CI_CICR2_BFPW_SMASK= 0x3F,
        XLLP_CI_CICR2_RESERVED  = 0x00000200,
        XLLP_CI_CICR2_HSW_SHIFT = 10,
        XLLP_CI_CICR2_HSW_SMASK = 0x3F,
        XLLP_CI_CICR2_ELW_SHIFT = 16,
        XLLP_CI_CICR2_ELW_SMASK = 0xFF,
        XLLP_CI_CICR2_BLW_SHIFT = 24,
        XLLP_CI_CICR2_BLW_SMASK = 0xFF
};

enum XLLP_CI_REGBITS_CICR3 {
    XLLP_CI_CICR3_LPF_SHIFT = 0,
    XLLP_CI_CICR3_LPF_SMASK = 0x7FF,
    XLLP_CI_CICR3_VSW_SHIFT = 11,
    XLLP_CI_CICR3_VSW_SMASK = 0x1F,
    XLLP_CI_CICR3_EFW_SHIFT = 16,
    XLLP_CI_CICR3_EFW_SMASK = 0xFF,
    XLLP_CI_CICR3_BFW_SHIFT = 24,
    XLLP_CI_CICR3_BFW_SMASK = 0xFF
};

enum XLLP_CI_REGBITS_CICR4 {
    XLLP_CI_CICR4_DIV_SHIFT = 0,
    XLLP_CI_CICR4_DIV_SMASK = 0xFF,
    XLLP_CI_CICR4_FR_RATE_SHIFT = 8,
    XLLP_CI_CICR4_FR_RATE_SMASK = 0x7,
    XLLP_CI_CICR4_RESERVED1 = 0x0007F800,
    XLLP_CI_CICR4_MCLK_EN   = 0x00080000,
    XLLP_CI_CICR4_VSP       = 0x00100000,
    XLLP_CI_CICR4_HSP       = 0x00200000,
    XLLP_CI_CICR4_PCP       = 0x00400000,
    XLLP_CI_CICR4_PCLK_EN   = 0x00800000,
    XLLP_CI_CICR4_RESERVED2 = 0xFF000000,
    XLLP_CI_CICR4_RESERVED  = XLLP_CI_CICR4_RESERVED1 | XLLP_CI_CICR4_RESERVED2
};

enum XLLP_CI_REGBITS_CISR {
    XLLP_CI_CISR_IFO_0      = 0x00000001,
    XLLP_CI_CISR_IFO_1      = 0x00000002,
    XLLP_CI_CISR_IFO_2      = 0x00000004,
    XLLP_CI_CISR_EOF        = 0x00000008,
    XLLP_CI_CISR_SOF        = 0x00000010,
    XLLP_CI_CISR_CDD        = 0x00000020,
    XLLP_CI_CISR_CQD        = 0x00000040,
    XLLP_CI_CISR_PAR_ERR    = 0x00000080,
    XLLP_CI_CISR_EOL        = 0x00000100,
    XLLP_CI_CISR_FEMPTY_0   = 0x00000200,
    XLLP_CI_CISR_FEMPTY_1   = 0x00000400,
    XLLP_CI_CISR_FEMPTY_2   = 0x00000800,
    XLLP_CI_CISR_RDAV_0     = 0x00001000,
    XLLP_CI_CISR_RDAV_1     = 0x00002000,
    XLLP_CI_CISR_RDAV_2     = 0x00004000,
    XLLP_CI_CISR_FTO        = 0x00008000,
    XLLP_CI_CISR_RESERVED   = 0xFFFF0000
};

enum XLLP_CI_REGBITS_CIFR {
    XLLP_CI_CIFR_FEN0       = 0x00000001,
    XLLP_CI_CIFR_FEN1       = 0x00000002,
    XLLP_CI_CIFR_FEN2       = 0x00000004,
    XLLP_CI_CIFR_RESETF     = 0x00000008,
    XLLP_CI_CIFR_THL_0_SHIFT= 4,
    XLLP_CI_CIFR_THL_0_SMASK= 0x3,
    XLLP_CI_CIFR_RESERVED1  = 0x000000C0,
    XLLP_CI_CIFR_FLVL0_SHIFT= 8,
    XLLP_CI_CIFR_FLVL0_SMASK= 0xFF,
    XLLP_CI_CIFR_FLVL1_SHIFT= 16,
    XLLP_CI_CIFR_FLVL1_SMASK= 0x7F,
    XLLP_CI_CIFR_FLVL2_SHIFT= 23,
    XLLP_CI_CIFR_FLVL2_SMASK= 0x7F,
    XLLP_CI_CIFR_RESERVED2  = 0xC0000000,
    XLLP_CI_CIFR_RESERVED   = XLLP_CI_CIFR_RESERVED1 | XLLP_CI_CIFR_RESERVED2
};

//---------------------------------------------------------------------------
//     Parameter Type definitions
//---------------------------------------------------------------------------
typedef enum  {
        XLLP_CI_RAW8 = 0,                   //RAW
        XLLP_CI_RAW9,
        XLLP_CI_RAW10,
        XLLP_CI_YCBCR422,               //YCBCR
        XLLP_CI_YCBCR422_PLANAR,        //YCBCR Planaried
        XLLP_CI_RGB444,                 //RGB
        XLLP_CI_RGB555,
        XLLP_CI_RGB565,
        XLLP_CI_RGB666,
        XLLP_CI_RGB888,
        XLLP_CI_RGBT555_0,              //RGB+Transparent bit 0
        XLLP_CI_RGBT888_0,
        XLLP_CI_RGBT555_1,              //RGB+Transparent bit 1
        XLLP_CI_RGBT888_1,
        XLLP_CI_RGB666_PACKED,          //RGB Packed
        XLLP_CI_RGB888_PACKED,
        XLLP_CI_INVALID_FORMAT = 0xFF
} XLLP_CI_IMAGE_FORMAT;

typedef enum {
    XLLP_CI_INTSTATUS_IFO_0      = 0x00000001,
    XLLP_CI_INTSTATUS_IFO_1      = 0x00000002,
    XLLP_CI_INTSTATUS_IFO_2      = 0x00000004,
    XLLP_CI_INTSTATUS_EOF        = 0x00000008,
    XLLP_CI_INTSTATUS_SOF        = 0x00000010,
    XLLP_CI_INTSTATUS_CDD        = 0x00000020,
    XLLP_CI_INTSTATUS_CQD        = 0x00000040,
    XLLP_CI_INTSTATUS_PAR_ERR    = 0x00000080,
    XLLP_CI_INTSTATUS_EOL        = 0x00000100,
    XLLP_CI_INTSTATUS_FEMPTY_0   = 0x00000200,
    XLLP_CI_INTSTATUS_FEMPTY_1   = 0x00000400,
    XLLP_CI_INTSTATUS_FEMPTY_2   = 0x00000800,
    XLLP_CI_INTSTATUS_RDAV_0     = 0x00001000,
    XLLP_CI_INTSTATUS_RDAV_1     = 0x00002000,
    XLLP_CI_INTSTATUS_RDAV_2     = 0x00004000,
    XLLP_CI_INTSTATUS_FTO        = 0x00008000,
    XLLP_CI_INTSTATUS_ALL       = 0x0000FFFF
} XLLP_CI_INTERRUPT_STATUS;

typedef enum {
    XLLP_CI_INT_IFO      = 0x00000001,
    XLLP_CI_INT_EOF      = 0x00000002,
    XLLP_CI_INT_SOF      = 0x00000004,
    XLLP_CI_INT_CDD      = 0x00000008,
    XLLP_CI_INT_CQD      = 0x00000010,
    XLLP_CI_INT_PAR_ERR  = 0x00000020,
    XLLP_CI_INT_EOL      = 0x00000040,
    XLLP_CI_INT_FEMPTY   = 0x00000080,
    XLLP_CI_INT_RDAV     = 0x00000100,
    XLLP_CI_INT_FTO      = 0x00000200,
    XLLP_CI_INT_ALL      = 0x000003FF
} XLLP_CI_INTERRUPT_MASK;
#define XLLP_CI_INT_MAX 10

typedef enum XLLP_CI_MODE {
        XLLP_CI_MODE_MP,             // Master-Parallel
        XLLP_CI_MODE_SP,             // Slave-Parallel
        XLLP_CI_MODE_MS,             // Master-Serial
        XLLP_CI_MODE_EP,             // Embedded-Parallel
        XLLP_CI_MODE_ES              // Embedded-Serial
} XLLP_CI_MODE;


typedef enum  {
        XLLP_CI_FR_ALL = 0,          // Capture all incoming frames
        XLLP_CI_FR_1_2,              // Capture 1 out of every 2 frames
        XLLP_CI_FR_1_3,              // Capture 1 out of every 3 frames
        XLLP_CI_FR_1_4,
        XLLP_CI_FR_1_5,
        XLLP_CI_FR_1_6,
        XLLP_CI_FR_1_7,
        XLLP_CI_FR_1_8
} XLLP_CI_FRAME_CAPTURE_RATE;


typedef enum  {
        XLLP_CI_FIFO_THL_32 = 0,
        XLLP_CI_FIFO_THL_64,
        XLLP_CI_FIFO_THL_96
} XLLP_CI_FIFO_THRESHOLD;

typedef struct {
    unsigned int BFW;
    unsigned int BLW;
} XLLP_CI_MP_TIMING, XLLP_CI_MS_TIMING;

typedef struct {
    unsigned int BLW;
    unsigned int ELW;
    unsigned int HSW;
    unsigned int BFPW;
    unsigned int FSW;
    unsigned int BFW;
    unsigned int EFW;
    unsigned int VSW;
} XLLP_CI_SP_TIMING;

typedef enum {
    XLLP_CI_DATA_WIDTH4 = 0x0,
    XLLP_CI_DATA_WIDTH5 = 0x1,
    XLLP_CI_DATA_WIDTH8 = 0x2,
    XLLP_CI_DATA_WIDTH9 = 0x3,
    XLLP_CI_DATA_WIDTH10= 0x4
} XLLP_CI_DATA_WIDTH;

// Register address for OV9653
#define OV9653_PID		0x000A				// Product ID
#define OV9653_VER		0x000B				// Version

void I2C_Initial(void);
void I2C_Disable(void);
int  I2C_read_byte(char regaddr,char *buf);

////////////////////////////////////////////////////////////////////////////////
//
// Implemented code
//

void GpioSetDirection(int *pins, int direction)
{
    int i;
    int num = pins[0];

    for (i = 1; i <= num; i++) {
        xs_setgpio_dir(pins[i], direction);
    }
}

void GpioSetAlternateFn(int *pins, int* altfunc)
{
    int i;
    int num = pins[0];

    for (i = 1; i <= num; i++) {
        xs_setgpio_dir(pins[i], altfunc[i]);
    }
}

//config GPIO for camera
void CameraGpioSetup()
{
    const int inpins[]  = {11, 81, 55, 51, 50, 83, 48, 17, 12, 54,85,84};
    const int outpins[] = {4, 53, 57, 56, 120};
    const int altpins[] = {12, 81, 55, 51, 50, 83, 48, 17, 12, 53, 54, 85, 84};
    const int altfunc[] = {12, 2,  1,  1,  1,  3,  1,  2,  2,  2,  3,  3,  3};
    GpioSetDirection((int *)inpins, GPIO_PIN_IN);
    GpioSetDirection((int *)outpins, GPIO_PIN_OUT);
    GpioSetAlternateFn((int *)altpins, (int *)altfunc);
}

void CIInit()
{
    // clear all CI registers
    CICR0 = 0x3FF;   // disable all interrupts
    CICR1 = 0;
    CICR2 = 0;
    CICR3 = 0;
    CICR4 = 0;
    CISR = ~0;
    CIFR = 0;
    CITOR = 0;

    // enable CI clock
    CKEN |= XLLP_CLKEN_CAMERA;
}

void CIDeInit()
{
    // disable CI clock
    CKEN &= ~XLLP_CLKEN_CAMERA;
}


void CISetMode(XLLP_CI_MODE mode, XLLP_CI_DATA_WIDTH data_width)
{
    unsigned int value;

    // write mode field in cicr0
    value = CICR0;

    //set 0b000 = Master-parallel mode
    value &= ~(XLLP_CI_CICR0_SIM_SMASK << XLLP_CI_CICR0_SIM_SHIFT/*Sensor interface Mode*/);
    value |= (unsigned int)mode << XLLP_CI_CICR0_SIM_SHIFT;
    CICR0 = value;

    // write data width cicr1
    value = CICR1;
    value &= ~(XLLP_CI_CICR1_DW_SMASK << XLLP_CI_CICR1_DW_SHIFT);
    value |= ((unsigned)data_width) << XLLP_CI_CICR1_DW_SHIFT;
    CICR1 = value;
    return;
}

void CISetClock(int pclk_enable, int mclk_enable, unsigned int mclk_khz)
{
    unsigned int ciclk,  value, div, cccr_l;

    // determine the LCLK frequency programmed into the CCCR.
    cccr_l = CCCR & 0x0000001F;
	if (cccr_l < 8) // L = [2 - 7]
		ciclk = (13 * cccr_l) * 100;
	else if (cccr_l < 17) // L = [8 - 16]
		ciclk = ((13 * cccr_l) * 100) >> 1;
	else // if (cccr_l < 32) // L = [17 - 31]
		ciclk = ((13 * cccr_l) * 100) >> 2;

	// want a divisor that gives us a clock rate as close to, but not more than the given mclk.
	div = (ciclk / (2 * mclk_khz))-1;

    // write cicr4
    value = CICR4;
    value &= ~(XLLP_CI_CICR4_PCLK_EN | XLLP_CI_CICR4_MCLK_EN | XLLP_CI_CICR4_DIV_SMASK<<XLLP_CI_CICR4_DIV_SHIFT);
    value |= (pclk_enable) ? XLLP_CI_CICR4_PCLK_EN : 0;
    value |= (mclk_enable) ? XLLP_CI_CICR4_MCLK_EN : 0;
    value |= div << XLLP_CI_CICR4_DIV_SHIFT;
    CICR4 = value;

    return;
}

void CISetPolarity(int pclk_sample_falling, int hsync_active_low, int vsync_active_low)
{
    unsigned int value;

    // write cicr4
    value = CICR4;
    value &= ~(XLLP_CI_CICR4_PCP | XLLP_CI_CICR4_HSP | XLLP_CI_CICR4_VSP);
    value |= (pclk_sample_falling)? XLLP_CI_CICR4_PCP : 0;
    value |= (hsync_active_low) ? XLLP_CI_CICR4_HSP : 0;
    value |= (vsync_active_low) ? XLLP_CI_CICR4_VSP : 0;
    CICR4 = value;
    return;
}

void CISetFIFO(unsigned int timeout, XLLP_CI_FIFO_THRESHOLD threshold, int fifo1_enable,
                   int fifo2_enable)
{
    unsigned int value;

    // write citor
    CITOR = timeout;

    // write cifr: always enable fifo 0! also reset input fifo
    value = CIFR;
    value &= ~(XLLP_CI_CIFR_FEN0 | XLLP_CI_CIFR_FEN1 | XLLP_CI_CIFR_FEN2 | XLLP_CI_CIFR_RESETF |
                XLLP_CI_CIFR_THL_0_SMASK<<XLLP_CI_CIFR_THL_0_SHIFT);
    value |= (unsigned int)threshold << XLLP_CI_CIFR_THL_0_SHIFT;
    value |= (fifo1_enable) ? XLLP_CI_CIFR_FEN1 : 0;
    value |= (fifo2_enable) ? XLLP_CI_CIFR_FEN2 : 0;
    value |= XLLP_CI_CIFR_RESETF | XLLP_CI_CIFR_FEN0;
    CIFR = value;
    return;
}

void SensorPowerDown(int powerDown)
{
    //power on.
    if( powerDown == 1 ) {
        xs_setgpio_outhigh(56);
    }
    else {
        xs_setgpio_outlow(56);
    }

    udelay(50);
}

void SensorReset()
{
    //reset
    xs_setgpio_outhigh(57);
    udelay(10);
    xs_setgpio_outlow(57);
}

int Do_Test_Camera(char *param)
{
    char id, rev;

    CameraGpioSetup();

	//capture interface init
    CIInit();

    // Configure CI according to sensor's hardware
    // master parallel with 8 data pins
    CISetMode(XLLP_CI_MODE_MP/*0*/, XLLP_CI_DATA_WIDTH8/*0x2*/);

    // enable pixel clock(sensor will provide pclock) and master clock = 26MHZ
    CISetClock(1, 1, 2600); // 26MHz

    // data sample on rising and h,vsync active high
    CISetPolarity(0, 0, 0);

    // fifo control, XLLP_CI_FIFO_THL_32 -> 32 bytes or
    // more in FIFO_0 causes a DMA request.
    CISetFIFO(0/*time out*/, XLLP_CI_FIFO_THL_32, 1, 1);

    // sensor power on and reset
    SensorPowerDown(0);
    SensorReset();
    udelay(10*1000);

    //initialize the I2C
    I2C_Initial();
    udelay(10*1000);

    // Test sensor by read out product id and version
    int status = I2C_read_byte(OV9653_PID, &id);
    status=I2C_read_byte(OV9653_VER, &rev);

    // Output camera sensor ID
    printk("\nGot Camera pid:0x%x, rev:0x%x\n\n",id,rev) ;

#if 0
    SensorInit(pCameraCxt);
#endif

    udelay(10*1000);

    // Close I2C
    I2C_Disable();
    CIDeInit();

    return 0;
}


